TFuzzyCMeans = class(TObject)
  private
    Weight: TVector2DReal;          // Pesos
    Output: TVectorReal;            // Saídas
    Input: TVectorReal;             // Entradas
    procedure UpdateWeights;
    procedure UpdateLearnRate;
  public
    NumInputs: Integer;             // Número de entradas
    NumOutputs: Integer;            // Número de saídas
    WinOutput: Integer;             // Saída vencedora
    NumIt: Integer;                 // Número de iterações
    MaxNumIt: Integer;              // Número máximo de iterações
    Exponent: Integer;              // Expoente de peso
    LearnRate: Double;              // Taxa de aprendizado
    NetFileName: String;            // Arquivo de rede
    SetFileName: String;            // Arquivo do conjunto de treino
    xtInput: array of TVectorReal;  // Conjunto de entradas de treino
    constructor Create; virtual;
    destructor Destroy; override;
    procedure CreateNet;
    procedure DeleteNet;
    procedure DeleteSet;
    procedure LoadSetFromFile;
    procedure GetOutput(var yOutput: TVectorReal);
    procedure SaveNet;
    procedure LoadNet;
    procedure Compute(xInput: TVectorReal);
    procedure Training;
    procedure TrainingOnLinePattern(xInput: TVectorReal);
    procedure AddPattern(xInput: TVectorReal);
  end;


//------------------------------------------------------------------------------
// TFuzzyCMeans
//------------------------------------------------------------------------------


// Construtor

constructor TFuzzyCMeans.Create;
begin
SetLength(Weight,0);
SetLength(Output,0);
SetLength(Input,0);
SetLength(xtInput,0);
Randomize;
end;


// Destrutor

destructor TFuzzyCMeans.Destroy;
begin
Weight:=nil;
Output:=nil;
Input:=nil;
xtInput:=nil;
end;


// Criação do mapa nebuloso de C-médias

procedure TFuzzyCMeans.CreateNet;
var
   i,j: Integer;
begin
// Aloca memória para os pesos e para as saídas
SetLength(Weight,NumOutputs,NumInputs);
SetLength(Output,NumOutputs);
SetLength(Input,NumInputs);
// Inicializa pesos aleatórios
for i:=0 to NumOutputs-1 do
    for j:=0 to NumInputs-1 do
        Weight[i,j]:=Random(1);
end;


// Apaga o mapa

procedure TFuzzyCMeans.DeleteNet;
begin
SetLength(Weight,0);
SetLength(Input,0);
SetLength(Output,0);
NumInputs:=0;
NumOutputs:=0;
end;


// Apaga o conjunto de treinamento/teste

procedure TFuzzyCMeans.DeleteSet;
begin
SetLength(xtInput,0);
end;


// Carrega o conjunto de treinamento a partir do arquivo

procedure TFuzzyCMeans.LoadSetFromFile;
var
   Arq: TextFile;
   k,i: Integer;
begin
AssignFile(Arq,SetFileName);
Reset(Arq);
k:=0;
while not eof(Arq) do
      begin
      k:=k+1;
      // Carrega entradas
      SetLength(xtInput,k);
      SetLength(xtInput[k-1],NumInputs);
      for i:=0 to NumInputs-1 do
          Read(Arq,xtInput[k-1,i]);
      end;
CloseFile(Arq);
end;


// Pega valor da saída

procedure TFuzzyCMeans.GetOutput(var yOutput: TVectorReal);
var
   i: Integer;
begin
for i:=0 to NumOutputs-1 do
    yOutput[i]:=Output[i];
end;


// Salva a rede em arquivo

procedure TFuzzyCMeans.SaveNet;
var
   Arq: TextFile;
   i,j: Integer;
begin
AssignFile(Arq,NetFileName);
ReWrite(Arq);
WriteLn(Arq,Exponent);
WriteLn(Arq,LearnRate:8:5);
WriteLn(Arq,MaxNumIt);
WriteLn(Arq,NumInputs);
WriteLn(Arq,NumOutputs);
for i:=0 to NumOutputs-1 do
    for j:=0 to NumInputs-1 do
        WriteLn(Arq,Weight[i,j]:8:5);
CloseFile(Arq);
end;


// Carrega rede do arquivo

procedure TFuzzyCMeans.LoadNet;
var
   Arq: TextFile;
   i,j: Integer;
begin
AssignFile(Arq,NetFileName);
Reset(Arq);
ReadLn(Arq,Exponent);
ReadLn(Arq,LearnRate);
ReadLn(Arq,MaxNumIt);
ReadLn(Arq,NumInputs);
ReadLn(Arq,NumOutputs);
SetLength(Weight,NumOutputs,NumInputs);
SetLength(Output,NumOutputs);
SetLength(Input,NumInputs);
for i:=0 to NumOutputs-1 do
    for j:=0 to NumInputs-1 do
        ReadLn(Arq,Weight[i,j]);
CloseFile(Arq);
end;


// Calcula saída da rede

procedure TFuzzyCMeans.Compute(xInput: TVectorReal);
var
   i,j: Integer;
   Outputn: array of Double;
begin
// Pega entradas
for i:=0 to NumInputs-1 do
    Input[i]:=xInput[i];
// Calcula saídas
SetLength(Outputn,NumOutputs);
for i:=0 to NumOutputs-1 do
    begin
    Outputn[i]:=0.0000001;
    for j:=0 to NumInputs-1 do
        Outputn[i]:=Outputn[i]+sqr(Input[j]-Weight[i,j]);
    end;
for i:=0 to NumOutputs-1 do
    begin
    Output[i]:=0;
    for j:=0 to NumOutputs-1 do
        Output[i]:=Output[i]+Power(Outputn[i]/Outputn[j],1/(Exponent-1));
    Output[i]:=1/Output[i];
    end;
Outputn:=nil;
// Acha o neurônio vencedor
WinOutput:=0;
for i:=1 to NumOutputs-1 do
    if Output[i]>Output[WinOutput] then
       WinOutput:=i;
end;


// Atualiza os pesos da rede

procedure TFuzzyCMeans.UpdateWeights;
var
   i,j: Integer;
   PF: Double;   // Função de pertinência
begin
UpdateLearnRate;
i:=WinOutput;
PF:=Power(Output[i],Exponent);
for j:=0 to NumInputs-1 do
    Weight[i,j]:=Weight[i,j]+LearnRate*PF*(Input[j]-Weight[i,j]);
end;


// Atualiza a taxa de aprendizado

procedure TFuzzyCMeans.UpdateLearnRate;
begin
LearnRate:=LearnRate*exp(-1/1000);
if LearnRate<0.01 then
   LearnRate:=0.01;
end;


// Treinamento da rede

procedure TFuzzyCMeans.Training;
var
   k: Integer;
begin
NumIt:=0;
while NumIt<MaxNumIt do
      begin
      for k:=0 to Length(xtInput)-1 do
          begin
          Compute(xtInput[k]);
          UpdateWeights;
          end;
      NumIt:=NumIt+1;
      end;
end;


// Adiciona padrão ao conjunto de treinamento

procedure TFuzzyCMeans.AddPattern(xInput: TVectorReal);
var
   k: Integer;
begin
// Empilha padrão
SetLength(xtInput,Length(xtInput)+1);
SetLength(xtInput[Length(xtInput)-1],NumInputs);
// Preenche padrão
for k:=0 to NumInputs-1 do
    xtInput[Length(xtInput)-1,k]:=xInput[k];
end;


// Treinamento seqüencial: cada elemento é apresentado por vez

procedure TFuzzyCMeans.TrainingOnLinePattern(xInput: TVectorReal);
begin
Compute(xInput);
UpdateWeights;
end;


